package com.wzh.yuvwater.muxer;

import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;

import com.wzh.yuvwater.encoder.AudioCodecManager;
import com.wzh.yuvwater.encoder.CameraWrapper;
import com.wzh.yuvwater.encoder.PauseManager;
import com.wzh.yuvwater.encoder.VideoCodecManager;
import com.wzh.yuvwater.utils.Logger1;

import java.io.IOException;

/**
 * 音视频合并管理
 */
public class MuxerManager {

    public static final int TRACK_VIDEO = 1;
    public static final int TRACK_AUDIO = 2;
    private volatile int videoTrackIndex = -1;
    private volatile int audioTrackIndex = -1;
    private volatile boolean muxerStart;

    private String TAG = "MediaMuxerRunnable";
    private static MuxerManager sInstance;

    private HandlerThread mHandlerThread;
    private MediaMuxer mediaMuxer;
    private MuxerHandler mHandler;
    private static final int MSG_STOP_MUXER = 1;
    private static final int MSG_RESTART_MUXER = 2;
    private static final int MSG_SET_FORMAT = 3;
    private static final int MSG_ADD_VIDEO_DATA = 51;
    private static final int MSG_ADD_AUDIO_DATA = 52;
    private static final int MSG_PAUSE_MUXER = 6;
    private static final int MSG_RESUME_MUXER = 7;

    class MuxerHandler extends android.os.Handler {
        public MuxerHandler(Looper looper) {
            super(looper);
        }
        //最后一帧写入时间，解决帧时间顺序不一致
        private long lastAudioOutputTimeStamp;

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case MSG_RESTART_MUXER:
                    String filePath = (String) msg.obj;
                    readyStart(filePath);
                    break;
                case MSG_SET_FORMAT:
                    addTrackIndex(msg.arg1, (MediaFormat) msg.obj);
                    break;
                case MSG_ADD_VIDEO_DATA:
                    try {
                        MuxerData data = (MuxerData) msg.obj;
                        mediaMuxer.writeSampleData(videoTrackIndex, data.getByteBuf(), data.getBufferInfo());
                    } catch (Exception e) {
                        Logger1.e(TAG, "写入视频数据失败!" + e.toString());
                    }
                    break;
                case MSG_ADD_AUDIO_DATA:
                    try {
                        MuxerData data = (MuxerData) msg.obj;
                        if (data.getBufferInfo().presentationTimeUs > lastAudioOutputTimeStamp) {
                            mediaMuxer.writeSampleData(audioTrackIndex, data.getByteBuf(), data.getBufferInfo());
                            lastAudioOutputTimeStamp = data.getBufferInfo().presentationTimeUs;
                        }
                    } catch (Exception e) {
                        Logger1.e(TAG, "写入音频数据失败!" + e.toString());
                    }
                    break;
                case MSG_PAUSE_MUXER:
                    pauseMuxer();
                    break;
                case MSG_RESUME_MUXER:
                    resumeMuxer();
                    break;
                case MSG_STOP_MUXER:
                    stopMuxer();
                    break;
                default:
                    break;
            }
        }
    }

    private MuxerManager() {
        mHandlerThread = new HandlerThread("muxerThread");
        mHandlerThread.start();

        mHandler = new MuxerHandler(mHandlerThread.getLooper());
    }

    public static MuxerManager getInstance() {
        if (sInstance == null) {
            synchronized (MuxerManager.class) {
                if (sInstance == null)
                    sInstance = new MuxerManager();
            }
        }
        return sInstance;
    }

    /**
     * 重新开始新视频录制
     *
     * @param filePath
     */
    public void sendReStartMuxerMsg(String filePath) {
        mHandler.obtainMessage(MSG_RESTART_MUXER, filePath).sendToTarget();
    }

    /**
     * 暂停视频录制
     */
    public void sendPauseMuxerMsg() {
        mHandler.obtainMessage(MSG_PAUSE_MUXER).sendToTarget();
    }

    /**
     * 暂停视频录制
     */
    public void sendResumeMuxerMsg() {
        mHandler.obtainMessage(MSG_RESUME_MUXER).sendToTarget();
    }

    /**
     * 发送添加track事件
     *
     * @param index
     * @param mediaFormat
     */
    public void sendAddTrackMsg(int index, MediaFormat mediaFormat) {
        mHandler.obtainMessage(MSG_SET_FORMAT, index, 0, mediaFormat).sendToTarget();
    }

    /**
     * 添加视频数据
     */
    public void sendWriteVideoDataMsg(MuxerData data) {
        if (isMuxerStart() && data != null) {
            mHandler.obtainMessage(MSG_ADD_VIDEO_DATA, data).sendToTarget();
        }
    }

    /**
     * 添加音频数据
     */
    public void sendWriteAudioDataMsg(MuxerData data) {
        if (isMuxerStart() && data != null) {
            mHandler.obtainMessage(MSG_ADD_AUDIO_DATA, data).sendToTarget();
        }
    }

    /**
     * 结束当前视频录制
     */
    public void sendStopMuxerMsg() {
        mHandler.obtainMessage(MSG_STOP_MUXER).sendToTarget();
    }

    private void readyStart(String filePath) {
        Logger1.d(TAG, "readyStart start path=%s", filePath);
        muxerStart = false;
        videoTrackIndex = -1;
        audioTrackIndex = -1;

        try {
            mediaMuxer = new MediaMuxer(filePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
        } catch (IOException e) {
            Logger1.e(TAG, "MediaMuxer:", e);
            return;
        }
        PauseManager.getInstance().reset();
        VideoCodecManager.getInstance().initCodecManager(CameraWrapper.SRC_VIDEO_WIDTH,
                CameraWrapper.SRC_VIDEO_HEIGHT, CameraWrapper.getInstance().isPort() ? 270 : 0);
        VideoCodecManager.getInstance().startMediaCodec();
        AudioCodecManager.getInstance().startRecording();
        Logger1.d(TAG, "readyStart end ");
    }


    private void addTrackIndex(int index, MediaFormat mediaFormat) {
        if (mediaMuxer == null) {
            return;
        }
        /*轨迹改变之后,重启混合器*/
        if (index == TRACK_VIDEO && videoTrackIndex == -1) {
            try {
                videoTrackIndex = mediaMuxer.addTrack(mediaFormat);
                Logger1.i(TAG, "添加视轨 完成");
            } catch (Exception e) {
                Logger1.e(TAG, "addTrack 异常:" + e.toString());
            }
        }
        /*轨迹改变之后,重启混合器*/
        if (index == TRACK_AUDIO && audioTrackIndex == -1) {
            try {
                audioTrackIndex = mediaMuxer.addTrack(mediaFormat);
                Logger1.i(TAG, "添加音轨 完成");
            } catch (Exception e) {
                Logger1.e(TAG, "addTrack 异常:" + e.toString());
            }
        }

        if (videoTrackIndex != -1 && audioTrackIndex != -1) {
            mediaMuxer.start();
            //启动完成后才将状态变为true
            muxerStart = true;
        }
    }

    private boolean isMuxerStart() {
        return muxerStart;
    }

    /**
     * 暂停录制
     */
    private void pauseMuxer() {
        PauseManager.getInstance().pause();
        VideoCodecManager.getInstance().pauseMediaCodec();
        AudioCodecManager.getInstance().pauseMediaCodec();
    }

    /**
     * 继续录制
     */
    private void resumeMuxer() {
        PauseManager.getInstance().resume();
        VideoCodecManager.getInstance().resumeMediaCodec();
    }

    /**
     * 关闭mediaMuxer
     * create at 2017/3/22 17:59
     */
    private void stopMuxer() {
        Logger1.d(TAG, "releaseMuxer start " + mediaMuxer);
        if (mediaMuxer != null) {
            VideoCodecManager.getInstance().stopRecording();
            AudioCodecManager.getInstance().stopRecording();
            try {
                mediaMuxer.release();
            } catch (Exception e) {
                Logger1.e(TAG, "mediaMuxer.release() 异常:" + e.toString());
            }
            mediaMuxer = null;
        }

        Logger1.i(TAG, "releaseManager: start");
        if (mHandlerThread != null) {
            mHandlerThread.quit();
        }
        mHandler = null;
        sInstance = null;
        Logger1.i(TAG, "releaseManager: end");
    }

    public interface VideoCallback {
        /**
         * 图像处理后，准备写入文件时的回调（主要用于在界面上显示）
         */
        void after(byte[] outData);
    }

    public interface AudioCallback {
        /**
         * 音频的回调
         */
        void after(byte[] outData);
    }
}
